#include "physics.h"
#include "pBody.h"
#include "pMaterial.h"
#include "pCollision.h"
#include "pJoint.h"
#include "template.h"
#include "pPlayer.h"
#include "pWeb.h"


using namespace Tmpl8;
Physics* Physics::mInstance = 0;

Physics::Physics()
{
	mEntityIdx = 0;

	InitWorld();
	InitMaterials();
	
	mCollisionHandler = new pCollisionHandler();
	mWorld->SetContactListener(mCollisionHandler);
	
	mInstance = pEntity::mPhysics = this;
	

	mWeb = CreateWeb();
//	AddDamageTrigger(vector3(50.0f, 50.0f, 0.0f), vector3(500.0f, 1500.0f, 0.0f), 0.0f, PMODE_STATIC);
}

void Physics::Tick()
{
	float32 timeStep = 1.0f / 60.0f;	
	int32 velocityIterations = 10;
	int32 positionIterations = 10;

	mWorld->Step(timeStep, velocityIterations, positionIterations);
	mWorld->ClearForces();
	
	for (unsigned int i = 0; i < mEntityIdx; i++)
	{
		mEntities[i]->Tick();
	}

	mPlayer->Tick();
	mWeb->Tick();	
}


void Physics::Render()
{
	for (unsigned int i = 0; i < mEntityIdx; i++)
	{
		mEntities[i]->Draw();
	}

	mPlayer->Draw();
}

pBody* Physics::CreateBody(vector3 aPos, float aAngle, PHYSICS_MODE aMode)
{
	mEntities[mEntityIdx] = new pBody(mEntityIdx, aPos, aAngle, aMode);
	mEntityIdx++;
	return (pBody*)mEntities[mEntityIdx - 1];
}


void Physics::InitWorld()
{
	b2Vec2 gravity(0.0f, 0.1f);
	bool doSleep = true;
	mWorld = new b2World(gravity, doSleep);
}

void Physics::InitPlayer()
{
	

}

pMaterial* Physics::GetMaterial(std::string aName)
{
	std::map<std::string, pMaterial*>::iterator it;
	it = mMaterials.find(aName);
	if (it == mMaterials.end())
	{
		return mMaterials[std::string("default")];
	}
	else
	{
		return it->second;
	}
}


pBody* Physics::GetBodyByName( std::string aName )
{
	for(unsigned int i = 0; i < mEntityIdx; i++)
	{
		if(mEntities[i]->GetName() == aName)
		{
			pBody* body = (pBody*)mEntities[i];
			return body;
		}
	}
	return 0;
}

pWeb* Physics::CreateWeb()
{
	mWeb = new pWeb();
	return mWeb;
}

pPlayer* Physics::CreatePlayer(vector3 aPos)
{
	mPlayer = new pPlayer(aPos ,mWeb);
	mWeb->SetPlayer(mPlayer);
	return mPlayer;
}

pJoint* Physics::CreateJoint( JointProperties* aJointProperties)
{
	pBody* body1 = GetBodyByName(aJointProperties->body1);
	pBody* body2 = GetBodyByName(aJointProperties->body2);
	b2Joint* joint = 0;
	if(std::string(aJointProperties->type) == "distance")
	{
		b2DistanceJointDef jointDef;

		b2Vec2 vec1 = b2Vec2((aJointProperties->anchorpoint1->x * SCALE_IN) + body1->GetPosition().x, (aJointProperties->anchorpoint1->y * SCALE_IN) + body1->GetPosition().y);
		b2Vec2 vec2 = b2Vec2((aJointProperties->anchorpoint2->x * SCALE_IN) + body2->GetPosition().x, (aJointProperties->anchorpoint2->y * SCALE_IN) + body2->GetPosition().y);

		jointDef.Initialize(body1->Get2DBoxBody(), body2->Get2DBoxBody(), vec1, vec2);

		jointDef.frequencyHz = aJointProperties->frequency;

		jointDef.dampingRatio = aJointProperties->dampingratio;

		jointDef.collideConnected = aJointProperties->collideconnected;

		jointDef.length = aJointProperties->length * SCALE_IN;

		joint = mWorld->CreateJoint(&jointDef);
	}
	else if(std::string(aJointProperties->type) == "revolute")
	{
		b2RevoluteJointDef jointDef;

		b2Vec2 anchor;
		anchor.x = body1->GetPosition().x + (aJointProperties->anchorpoint1->x * SCALE_IN);
		anchor.y = body1->GetPosition().y + (aJointProperties->anchorpoint1->y * SCALE_IN);
		jointDef.Initialize(body1->Get2DBoxBody(), body2->Get2DBoxBody(), anchor);

		jointDef.lowerAngle = aJointProperties->lowerAngle; 

		jointDef.upperAngle = aJointProperties->upperAngle;

		jointDef.enableLimit = aJointProperties->enablelimit;

		jointDef.maxMotorTorque = aJointProperties->maxMotorTorque;

		jointDef.motorSpeed = aJointProperties->motorspeed;

		jointDef.collideConnected = aJointProperties->collideconnected;

		jointDef.enableMotor = aJointProperties->enablemotor;

		joint = mWorld->CreateJoint(&jointDef);
	}

	if(joint)
	{
		mEntities[mEntityIdx] = new pJoint(mEntityIdx, joint, body1, body2);
		mEntityIdx++;
		return (pJoint*)mEntities[mEntityIdx-1];
	}
	return 0;
}

void Physics::SetPointerToCam( vector3* aPointerToCam )
{
	mCamOffset = aPointerToCam;
}

void Physics::InitMaterials()
{
	b2FixtureDef tmp = b2FixtureDef();

	//Create default material
	tmp.density = 30.0f;
	tmp.friction = 0.1f;
	tmp.isSensor = false;
	tmp.restitution = 0.2f;
	pMaterial* mat = new pMaterial(tmp);
	mat->SetCanStick(true);
	mat->SetDamage(0);
	mat->SetCanJump(true);
	mMaterials["default"] = mat;

	//Create water material
	tmp.density = 30.0f;
	tmp.friction = 0.5f;
	tmp.isSensor = false;
	tmp.restitution = 0.2f;
	pMaterial* watermat = new pMaterial(tmp);
	watermat->SetCanStick(false);
	watermat->SetDamage(0);
	watermat->SetCanJump(true);
	mMaterials["water"] = watermat;

	//Create default no stick
	tmp.density = 30.0f;
	tmp.friction = 0.5f;
	tmp.isSensor = false;
	tmp.restitution = 0.2f;
	mat = new pMaterial(tmp);
	mat->SetCanStick(false);
	mat->SetDamage(0);
	mat->SetCanJump(true);
	mMaterials["defaultNoStick"] = mat;

	//Create damage material
	tmp.density = 30.0f;
	tmp.friction = 0.5f;
	tmp.isSensor = false;
	tmp.restitution = 0.2f;
	mat = new pMaterial(tmp);
	mat->SetCanStick(false);
	mat->SetDamage(0.1f);
	mat->SetCanJump(true);
	mMaterials["damage"] = mat;

	tmp.density = 30.0f;
	tmp.friction = 0.5f;
	tmp.isSensor = false;
	tmp.restitution = 0.2f;
	mat = new pMaterial(tmp);
	mat->SetCanStick(false);
	mat->SetDamage(0);
	mat->SetCanJump(false);
	mMaterials["defaultNoJump"] = mat;	
	
}

pPlayer* Tmpl8::Physics::GetPlayer()
{
	return mPlayer;
}

